home *** CD-ROM | disk | FTP | other *** search
Text File | 1992-07-29 | 46.8 KB | 1,795 lines |
- // c.e
- //
- // C code editing commands for Epsilon 6.00
- //
- // Copyright (C) 1985, 1986, 1987 Free Software Foundation, Inc.
- // Copyright (C) 1991, K. Shane Hartman and the Free Software
- // Foundation, Inc.
- //
- // This file was part of GNU Emacs but only superficially resembles
- // the original Emacs LISP code.
- //
- // This file is free software; you can redistribute it and/or modify
- // it under the terms of the GNU General Public License as published
- // by the Free Software Foundation; either version 1, or (at your
- // option) any later version.
- //
- // This file is distributed in the hope that it will be useful, but
- // WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- // General Public License for more details (the file COPYING)
- //
- // If you want a copy of the GNU General Public License, write to the
- // Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- //
- //
- // Most of this code was ported to EEL by me (KSH) from Gnu Emacs
- // lisp sources. The remainder was created by me to facilitate the
- // port. All code is Copyleft by K. Shane Hartman and the Free
- // Software Foundation. See the file COPYING for specific terms of
- // the license.
- //
- // This file is a straight replacement for Lugaru's C.E. Just
- // compile load and save state. You may want to play with the
- // control variables to get the style you want. See below.
- //
- // WARNING: The Gnu C indenter is very good, but searches a lot. If
- // your machine is wimpy, I would stick with Lugaru's indenter which
- // is faster and dumber. I use a 486/33 so I don't notice the extra
- // computation. Also, I use a limited lookback parser to handle most
- // of the simple cases which assumes the indentation style used in
- // this file. The general indenter is used for hairy cases. It is
- // disabled by default, but see variable c-indenter-shortcut.
- // Without the quick-indenter, large functions require noticeable but
- // tolerable time to indent. The reason is that Lugaru's regex
- // searches really suck. (07/10/92 JBK Ripped out the lookback
- // parser. The performance benefits have become less obvious as the
- // general indenter has been tuned over time).
- //
- // Note the commands (refer to C.DOC or the code for details):
- //
- // forward_cexp()
- // backward_cexp()
- // kill_cexp()
- // start_of_defun()
- // end_of_defun()
- // up_level()
- // indent_function()
- // mark_function()
- // list_functions()
- //
- // Revision History:
- //
- // Send bug reports, bug fixes to shane@ai.mit.edu
- //
- // Version 1.0: 06/01/91 shane@ai.mit.edu Initial version.
- //
- // Version 1.1: 06/20/91 shane@ai.mit.edu Some bug fixes.
- //
- // Version 1.2: 06/28/91 shane@ai.mit.edu Improve performance of
- // looking_at slightly.
- //
- // Version 1.3: 07/08/91 shane@ai.mit.edu Removed is_word_char,
- // use Lugaru's. Also added variable c_tab_always_indent.
- //
- // Version 1.4: 07/12/91 shane@ai.mit.edu Made C-M-A a little
- // smarter.
- //
- // Version 1.5: 07/19/91 shane@ai.mit.edu Fix problem with
- // indenting lines after disgusting c++ comments.
- //
- // Version 1.6: 07/18/91 More fun with c++ comments. Fix
- // c_backward_to_noncomment to understand them.
- //
- // Version 1.7: 07/18/91 More fun with c++ comments. Fix bug in
- // skip_c_comments to understand them. Make
- // c_backward_to_start_of_if stop if it detects a syntax error.
- // Stop binding case_fold many times. Do it once in outermost
- // call and use unwind-protect (C's miserable excuse for it,
- // that is).
- //
- // Version 1.8: 08/05/91 shane@ai.mit.edu Improve performance of
- // CR between functions if c_indenter_shortcut is T. Flush
- // c_backward_to_start_of_do, I don't think it does anything.
- // Do case folding in C-M-Q (indent-function). Halt infinite
- // loop for #thing ... \ at file begin. Make the effect of
- // c_brace_offset and c_case_offset more predictable for
- // settings I don't use. Remove unused variable
- // c_continued_brace_offset. Use c_brace_offset for this.
- //
- // Version 1.9: 08/12/91 johnk@wrq.com (John Kercheval) Fix the
- // indent-function command to use spots to remember the region
- // it is indenting since the region changes while indenting.
- // Use beginning_of_defun to determine the true function start
- // rather than the macro BEGINNING_OF_DEFUN(). Broke out
- // beginning_of_defun command guts and renamed command
- // beginning-of-defun to start-of-defun to support this (the
- // command recenters the window). Fix command do-c-indent to
- // handle tab correctly when !c_tab_always_indent and epsilon is
- // doing space to tab translation. Fix c_indenter_1 to handle
- // statement after case correctly (use current_indentation not
- // current_column).
- //
- // 08/12/91 shane@ai.mit.edu Fix indenter to work correctly when
- // there are no characters after the insertion point.
- //
- // Version 1.10: 08/13/91 shane@ai.mit.edu Restore
- // c_backward_to_start_of_do. It handles do - while statements
- // after ifs, etc.
- //
- // Version 1.11: 09/03/91 shane@ai.mit.edu CC is C++ file
- // extension. CPP is is C++ file extension. Fix bug with foo;
- // // comment in c_backward_to_noncomment.
- //
- // Version 1.12: 11/13/91 shane@ai.mit.edu Had to bind
- // do_c_newline to CTRL('M') in Epsilon 5.0.3, spec '\n' no
- // longer seems to invoke do-c-newline on enter. Added
- // mark-defun (I would put it on C-M-H but Epsilon treats this
- // the same as ALT-Backspace which sucks (they must have made
- // key handling compatible with ix). Made
- // calculate-simple-c-indent default to hairy indenter when it
- // detects "foo ( bar \n && ...". Ignore c-brace-offset if
- // brace is toplevel.
- //
- // Version 1.13: 11/14/91 shane@ai.mit.edu Made '{' and '}'
- // commands respect auto_indent variable. Changed mode name to
- // "NewC" rather than "GnuC". Added command list-defuns on A-#.
- // Fixed C++ comment bug reported by Caleb Epstein. Fixed C++
- // public: bug noted by mariogo@microsoft.com.
- //
- // Version 2.00: 07/10/92 johnk@wrq.com (John Kercheval) Remove
- // lots of bits and pieces of code hanging around. Use save_var
- // and save_spot for the protect unwind code and spot
- // allocations. Speed up and reduce size of looking_at(). Port
- // to V6.0 Epsilon sources. Removed the quick indent code in
- // favor of the full GNU indenter. Renamed list_defuns to
- // list_functions and modified to work with old style c function
- // declarations.
- //
- // Version 2.01: 07/20/92 johnk@wrq.com (John Kercheval) Fix bug
- // in skip_chars() when at the end of a buffer and called in
- // reverse direction. Fix a bug in parse_partial_cexp() dealing
- // with C++ style comments (the from and to parameters were
- // being ignored during the incomment state boolean release).
- // Fixed fundamental bug in do_backward_to_noncomment() which
- // prevented correct reverse searches over C++ style comments
- // (this included addition of a new state variable to mark the
- // beginning of the last comment).
- //
- // Version 2.02: 07/20/92 johnk@wrq.com (John Kercheval) Fix bug
- // in parse_partial_cexp() dealing with C++ style comments (one
- // of the edge cases required >= rather than >).
- //
- // Version 2.03: 07/21/92 johnk@wrq.com (John Kercheval) Add
- // several syntax elements which will result in an autoindent on
- // newline (was case, labels and a particular else and while
- // syntax). Those added were: general while, else, for, if and
- // switch syntaxes. Remove unneeded do loop and state query in
- // calculate_c_indent(). Fix bug in calculate_c_indent() which
- // would result in incorrect indent on first case within a
- // switch and incorrectly backed up over continued parameter
- // list. Comment indenter module in some detail. Fixed a bug
- // in parse_partial_cexp(), the terminating condition on the
- // while loop did not take into account multi-character search
- // criteria, the point to end location comparison was changed to
- // a matchstart to end location comparison. Moved to C++ style
- // line comments to aid in clarity and comment fills. Added the
- // boolean variable c_line_comment_align to facilitate in-line
- // C++ style comment blocks outside of the current indent level
- // similar to the built in behavior for standard C style
- // comments.
- //
- // Version 2.04: 07/22/92 johnk@wrq.com (John Kercheval) Fix a
- // newly introduced bug within calculate_c_indent(). The new
- // variable c_line_comment_align was affecting lines with
- // trailing comments as well as full line comments.
- //
- // Version 2.05: 07/29/92 johnk@wrq.com (John Kercheval) Fix a
- // bug in c_indenter_1() for the case where indenting an else
- // cuddling a brace. We were backing up one cexp to the opening
- // brace when we should have been backing up one more step to
- // the opening paren on the if block. Do some more commenting
- // and add the EDOC extension (C.DOC).
- //
-
-
- #include "eel.h"
-
- typedef struct pstate {
- int incomment;
- int beginning_of_comment;
- int instring;
- int level;
- int containing_cexp;
- int quoted;
- int beginning_of_defun;
- } PSTATE;
-
-
- ////////////////////////////////////////////////////////////////////////////
- //
- // The following variables control indentation style.
- //
-
- //
- // Indentation of C statements with respect to containing block.
- // This is in spaces but is filled with tabs if tab-size is smaller
- // than this value or the computed indent. This is the *standard*
- // indentation level.
- //
- int c_indent_level = 4;
-
- //
- // Imagined indentation of a C open brace that actually follows a
- // statement. This will result in an indentation for braced blocks
- // larger than that for continuation lines. This value affects only
- // code within the brace, not the brace indent itself.
- //
- int c_brace_imaginary_offset = 0;
-
- //
- // Extra indentation for braces, compared with other text in same
- // context. This value increases the depth of the brace. A brace
- // offset of 0 will line a brace on the following line at the same
- // indent level as the previous statement. This value specifies the
- // number of additional spaces to add to this indent.
- //
- int c_brace_offset = 0;
-
- //
- // Indentation level of declarations of C function arguments. This
- // specifies the indent level of non-ansi declarations in spaces.
- //
- int c_argdecl_indent = 4;
-
- //
- // Offset of C label lines (including C++ class keywords) relative to
- // usual indentation level. This level is normally a negative number.
- //
- int c_label_offset = -2;
-
- //
- // Offset of C case statements relative to usual indentation. The
- // case statement will add this indent offset to the case blocks
- // within the switch statement. The *usual* indentation is
- // c_indent_level as set above.
- //
- int c_case_offset = 0;
-
- //
- // Extra indent for lines not starting new statements such as
- // non-braced statements after while, if, for, etc. This does not
- // effect function parameters, paren expression, etc. (which are
- // lined up with the open paren).
- //
- int c_continued_statement_offset = 4;
-
- //
- // This BOOL value affects the alignment of the indent following a
- // line comment (C++). If this value is TRUE then the line following
- // a C++ line comment will be indented to be even with the previous
- // line if the previous line is also a non-trailing line comment
- // (blank lines *are* significant). This facilitates block and fill
- // comments which are not at normal indentation levels. If this
- // value is FALSE or if there is no non-trailing line comment on the
- // previous line then indent will procede normally. This value will
- // only affect behavior when a non-trailing comment is on the
- // previous line *and* is at a different indentation level than the
- // current code block. This is the non-modifiable default behavior
- // for C style comments.
- //
- int c_line_comment_align = 1;
-
- //
- // This BOOL value effects the global behavior of the indenter. A
- // non-zero value means TAB in C mode should always reindent the
- // current regardless of where in the line point is when the TAB
- // command is used.
- //
- int c_tab_always_indent = 0;
-
-
- ////////////////////////////////////////////////////////////////////////////
- //
- // Code Macros
- //
-
- #define LOOKING_AT(pat) looking_at (1, pat)
-
- #define INDENT_TO(indent) to_column(indent)
-
- #define SKIP_CHARS_BACKWARD(set) skip_chars (-1, (set))
- #define SKIP_CHARS_FORWARD(set) skip_chars (1, (set))
-
- #define FORWARD_SEXP() traverse_cexp (1)
- #define BACKWARD_SEXP() traverse_cexp (-1)
-
- #define SEARCH_FORWARD(str) search (1, (str))
- #define SEARCH_BACKWARD(str) search (-1, (str))
-
- #define BOLP() (current_column () == 0)
-
- #define BEGINNING_OF_DEFUN() \
- (re_search (-1, "^[{A-Za-z0-9$_]+[^A-Za-z0-9$_:]") ? \
- ((to_begin_line()), 1) : \
- ((point = 0), 0))
-
-
- ////////////////////////////////////////////////////////////////////////////
- //
- // skip_chars will move over all characters in set in the current
- // buffer in a particular direction
- //
-
- skip_chars(dir, set)
- int dir;
- char *set;
- {
- int last = size();
- int offset = dir < 0 ? -1 : 0;
-
- while (((point < last) || (dir == -1))
- && point > 0
- && index(set, character(point + offset)))
- point += dir;
- }
-
-
- ////////////////////////////////////////////////////////////////////////////
- //
- // current_indentation will return the value of the indentation of
- // the current line in the buffer.
- //
-
- current_indentation()
- {
- int indent;
-
- save_var point;
-
- to_indentation();
- indent = current_column();
- return (indent);
- }
-
-
- ////////////////////////////////////////////////////////////////////////////
- //
- // looking_at will return TRUE if the pattern in the given direction
- // is true beginning at the current point.
- //
-
- looking_at(dir, pat)
- int dir;
- char *pat;
- {
- save_spot point;
-
- if (parse_string(dir, pat, NULL))
- return 1;
- else
- return 0;
- }
-
-
- ////////////////////////////////////////////////////////////////////////////
- //
- // matching_end and associated macros will return the location of the
- // closest matching character in the forward direction.
- //
-
- char matching_ends[] = "][]}{})()\"\"''";
- char opening_ends[] = "[{(";
- char closing_ends[] = "]})";
-
- char
- matching_end(c)
- char c;
- {
- char *s = index(matching_ends, c);
-
- if (s == NULL)
- return (0);
- else
- return (s[1]);
- }
-
- #define opening_end(c) (index (opening_ends, (c)) != NULL)
-
- #define closing_end(c) (index (closing_ends, (c)) != NULL)
-
-
- ////////////////////////////////////////////////////////////////////////////
- //
- // is_slashified will return TRUE if the current character is a C
- // literal escaped by the '\'
- //
-
- is_slashified(dir)
- int dir;
- {
- int pos = point;
- int slash_count = 0;
-
- if (dir > 0)
- pos--;
- while (pos-- > 0 && character(pos) == '\\')
- slash_count++;
- return (slash_count & 1);
- }
-
-
- ////////////////////////////////////////////////////////////////////////////
- //
- // beginning_of_defun will move to the beginning of the current
- // function.
- //
-
- beginning_of_defun() {
- int last = point;
-
- while (BEGINNING_OF_DEFUN()) {
- int next = point;
- int save_excursion;
-
- if (character(point) == '{') {
- while (point > 0) {
- nl_reverse();
- save_excursion = point;
- to_begin_line();
- next = point;
- SKIP_CHARS_FORWARD(" \t");
- if (point == save_excursion) {
- nl_forward();
- break;
- }
- point = next;
- if (LOOKING_AT("#|(*.%*/)|(//)")) {
- nl_forward();
- break;
- }
- }
- break;
- }
- skip_c_comments(-1);
- save_excursion = point;
- to_begin_line();
- if (character(point) == '#') {
- point = next;
- break;
- }
- to_end_line();
- if (character(point - 1) == '\\') {
- point = next;
- break;
- }
- last = next;
- point = save_excursion;
- if ((character(point) == '}')
- || ((character(point) == ';')
- && (point > 0)
- && ((character(point - 1) == '}')
- || (character(point - 1) == ')')
- || (BACKWARD_SEXP(),
- skip_c_comments(-1),
- character(point) == '=')))) {
- point = last;
- break;
- }
- if (point == 0)
- break;
- to_end_line();
- point++;
- }
- }
-
-
- ////////////////////////////////////////////////////////////////////////////
- //
- // skip_c_comments will move over all whitespace and C/C++ comments
- // in the given direction starting at point in current buffer.
- //
-
- skip_c_comments(dir)
- int dir;
- {
- int offset = 1;
- int last = size();
-
- if (dir != 1) {
- dir = -1;
- offset = 0;
- }
- while (re_search(dir, "[^ \t\n\f]")
- && point > 0
- && point < last) {
- char c = character(point - offset);
- char cc = character(point - offset + dir);
-
- if (c == '/') {
- if (cc == '*')
- search(dir, dir > 0 ? "*/" : "/*");
- else if (cc == '/') { /* c++ comment */
- if (dir > 0)
- nl_forward();
- else
- point--;
- }
- } else {
- if (dir > 0)
- point -= dir;
- break;
- }
- }
- }
-
-
- ////////////////////////////////////////////////////////////////////////////
- //
- // traverse_cexp will move in the given direction to the end of the
- // current subexpression delineated by some C/C++ delimeter.
- //
- // KSH - This should use parse_partial_cexp but I did not port some
- // of the functionality it should have to do this. This is probably
- // faster and I have no more time to devote to this project, so I
- // have left it.
- //
- // JBK - This should indeed use some sort of partial expression
- // parser. The current implementation does not handle lone single or
- // double quotes or closing C comment characters within C++ style
- // line comments when traversing backwards. If such a character
- // pattern is found within the line comment then several things may
- // result but all will result in incorrect indentation. Speed issues
- // and a lack of interest (I just do not generally use these
- // character patterns in comments) prevent me from making the
- // required modifications. Any volunteers? Wed, 07/29/1992 23:08:40
- //
-
- traverse_cexp(dir)
- int dir;
- {
- int level = 0;
- int orig = point;
- char c = 0;
- char start = 0;
- char end = 0;
- int offset = 1;
- char buf[2];
- char patbuf[32];
-
- if (dir != 1) {
- dir = -1;
- offset = 0;
- }
- if (dir > 0
- && point < size()
- && index(":;?,", character(point)))
- point++;
- skip_c_comments(dir);
-
- //
- // if we are not already on one of the open or close end
- // delimiters move to the nearest one in the particular
- // direction.
- //
- if (!index(matching_ends, character(point))) {
- if (re_search(dir, "[][)(}{\"';:,? \t\n\f]")) {
- point -= dir;
- return (1);
- }
- return (0);
- } else if ((opening_end(character(point)) && dir > 0)
- || (closing_end(character(point)) && dir < 0)
- || character(point) == '"'
- || character(point) == '\'') {
- //
- // Move through code and comments until we get to the next
- // c subexpression.
- //
- if (dir < 0)
- point++;
- strcpy(patbuf, "[][)(}{\"']|/%*|%*/|//");
- while (re_search(dir, patbuf)) {
-
- // here should be parse_partial_cexp() calls
-
- buf[0] = c = character(point - offset);
- buf[1] = 0;
- if (start == 0) {
- start = c;
- end = matching_end(start);
- if (!end)
- start = 0;
- else {
- strcpy(patbuf, "%X|%X|[\"']|/%*|%*/|//");
- patbuf[1] = start;
- patbuf[4] = end;
- }
- }
- if (c == '"' || c == '\'') {
- char strpat[8];
- strcpy(strpat, "%X|\n");
- strpat[1] = c;
- while (re_search(dir, buf) && is_slashified(dir));
- if (character(point - 1) == '\n')
- break;
- } else if (c == '*') {
- search(dir, dir > 0 ? "*/" : "/*");
- } else if (c == '/') { /* c++ comment */
- if (dir > 0)
- nl_forward();
- }
- if (c == start)
- level++;
- if (c == end && !--level)
- break;
- }
- return (level == 0 && start != 0);
- } else {
- point = orig;
- return (0);
- }
- }
-
-
- ////////////////////////////////////////////////////////////////////////////
- //
- // parse_partial_cexp will move through the region specified by from
- // and to and determine the current indent (or block) level and state
- // of that region.
- //
-
- parse_partial_cexp(from, to, state)
- int from;
- int to;
- PSTATE *state;
- {
- char c = 0;
- int stack[64];
-
- state->instring = 0;
- state->incomment = 0;
- state->beginning_of_comment = 0;
- state->containing_cexp = -1;
- state->quoted = 0;
- state->level = -1;
- point = from;
-
- //
- // parse through open and close delimiters until we get to the
- // end of the region. Track comments as well as current level
- // and level start location.
- //
- while (re_search(1, "[][(){}\"']|/%*|//") && (matchstart < to)) {
- c = character(point - 1);
- if (opening_end(c)) {
- //
- // just store the current level start, error check
- // internal stack.
- //
- state->level++;
- if (state->level > (sizeof(stack) / sizeof(stack[0])))
- error("Nesting too deep: %d:", state->level);
- stack[state->level] = point - 1;
- } else if (closing_end(c)) {
- //
- // decrement the stack level and use the previous
- // enclosing level as the current. Validate that the
- // level end characters match the opening delimiter.
- //
- if (state->level >= 0
- && matching_end(character(stack[state->level])) == c)
- state->level--;
- else if (state->level >= 0) {
- point = stack[state->level];
- error("Looking for %c", character(stack[state->level]));
- }
- } else if (c == '"' || c == '\'') {
- char strpat[8];
- //
- // within a string or an escaped character, move along
- //
- state->quoted = -1;
- strcpy(strpat, "[X\n]");
- strpat[1] = c;
- while (re_search(1, strpat)
- && is_slashified(1)
- && point < to);
- if (point < to)
- state->quoted = 0;
- } else if (c == '*' || c == '/') {
- //
- // C/C++ comment, pass over the comment and reset state
- // if the end of the comment is not past our end limit.
- //
- state->incomment = 1;
- state->beginning_of_comment = point-2;
- state->quoted = -2;
- if (c == '*') {
- SEARCH_FORWARD("*/");
- } else {
- nl_forward();
- }
- if (point <= to) {
- state->incomment = 0;
- state->beginning_of_comment = 0;
- state->quoted = 0;
- }
- }
- }
- if (state->level >= 0)
- state->containing_cexp = stack[state->level];
- }
-
-
- ////////////////////////////////////////////////////////////////////////////
- //
- // c_indenter_1 will indent the current line given an assumption of C
- // syntax.
- //
-
- c_indenter_1(state)
- PSTATE *state;
- {
- int indent;
- int pos;
-
- //
- // validate that we have a bit of whitespace at the end of the
- // buffer to avoid boundary issues in the rest of the system.
- //
- if (point == size()) {
- insert(' ');
- point--;
- }
-
- //
- // obtain the first best guess for the indent level before we
- // special case the current line.
- //
- pos = size() - point;
- indent = calculate_c_indent(state);
- to_begin_line();
- if (indent == -1)
- //
- // This is inside a string constant
- //
- indent = current_indentation();
- else if (indent == -2)
- //
- // This is currently in a comment
- //
- indent = calculate_c_indent_in_comment();
- else if (LOOKING_AT("[ \t]*#"))
- //
- // We are indenting a preprocessor directive which is always
- // at the left edge.
- //
- indent = 0;
- else {
- //
- // Standard code state, special case particular syntactic
- // structures.
- //
- SKIP_CHARS_FORWARD(" \t");
- if (LOOKING_AT("case[ \t'(]|default:")) {
- //
- // we are in a switch statement and indenting a case
- // keyword.
- //
- if (state->containing_cexp >= 0) {
- int save_excursion = point;
-
- point = state->containing_cexp;
- indent = current_indentation() +
- c_case_offset + c_indent_level;
- point = save_excursion;
- }
- if (indent < 0)
- indent = 0;
- } else if (LOOKING_AT("[A-Za-z0-9$_]+:")) {
- //
- // This is a label or C++ class keyword
- //
- indent += c_label_offset;
- if (indent < 0)
- indent = 0;
- } else if (LOOKING_AT("else[ \t\n]")) {
- int save_excursion = point;
- //
- // This is an else, match to the opening if
- //
- c_backward_to_start_of_if(state->beginning_of_defun);
- indent = current_indentation();
- point = save_excursion;
- } else if (LOOKING_AT("}[ \t]*else")) {
- int save_excursion = point;
- //
- // This is a cuddly else, we have special knowledge that
- // this else lines up with the if with the opening brace.
- //
- point++;
- BACKWARD_SEXP(); // to begin of open brace
- BACKWARD_SEXP(); // to begin of open paren
- indent = current_indentation();
- point = save_excursion;
- } else if (LOOKING_AT("while[ \t\n]")) {
- int save_excursion = point;
- //
- // This is a while, check if it is part of a do-while
- // loop. If it is not then break to the default else for
- // standard indent.
- //
- if (!c_backward_to_start_of_do(state->beginning_of_defun)) {
- point = save_excursion;
- goto next; // just use the default else
- }
- //
- // This is a `while' that ends a do-while.
- //
- indent = current_indentation();
- point = save_excursion;
- } else
- next:
- //
- // This is a standard C statement, do nothing special unless
- // this is an open or close brace.
- //
- if (character(point) == '}')
- indent -= (c_indent_level - c_brace_offset);
- else if (character(point) == '{')
- indent += c_brace_offset;
- }
- to_indentation();
- //
- // Check for the special case of the first or last opening brace
- // in the function.
- //
- if (character(point) == '{'
- && c_brace_offset
- && state->containing_cexp <= 0)
- indent -= c_brace_offset;
- else if (character(point) == '}') {
- int save_excursion = point;
-
- point = state->containing_cexp;
- if (BOLP())
- indent = 0;
- point = save_excursion;
- }
- //
- // do the indent only if we are not already there
- //
- if (current_column() != indent)
- INDENT_TO(indent);
- //
- // If initial point was within line's indentation, position after
- // the indentation. Else stay at same point in text.
- //
- if (size() - pos > point)
- point = size() - pos;
- return (indent);
- }
-
-
- ////////////////////////////////////////////////////////////////////////////
- //
- // c_indenter sets up the protect unwind code, inits the state
- // variable and calls the primary indenter.
- //
-
- c_indenter()
- {
- PSTATE state;
- jmp_buf this_level;
- jmp_buf *old_level = top_level;
- int ret;
-
- save_var case_fold;
-
- case_fold = 0;
- top_level = &this_level;
- if (ret = setjmp(top_level)) {
- restore_vars();
- top_level = old_level;
- longjmp(top_level, ret);
- }
- state.beginning_of_defun = -1;
- ret = c_indenter_1(&state);
- top_level = old_level;
- return ret;
- }
-
-
- ////////////////////////////////////////////////////////////////////////////
- //
- // c_backward_to_exp_start will move to the first character of the
- // line containing the beginning of the current sub expression.
- //
-
- c_backward_to_exp_start(lim)
- int lim;
- {
- if (index(")\"", character(point - 1)))
- BACKWARD_SEXP();
- to_begin_line();
- if (point <= lim)
- point = lim + 1;
- SKIP_CHARS_FORWARD(" \t");
- }
-
-
- ////////////////////////////////////////////////////////////////////////////
- //
- // c_backward_to_start_of_do will if point follows a `do' statement,
- // move to beginning of it and return TRUE. Otherwise return FALSE
- // and don't move point.
- //
-
- c_backward_to_start_of_do(limit)
- int limit;
- {
- int save_excursion;
- int first = 1;
- int startpos = point;
- int done = 0;
-
- if (limit < 0)
- limit = 0;
-
- //
- // loop until we find an open do or move out of the current
- // enclosing c expression.
- //
- while (!done) {
- c_backward_to_noncomment(limit);
- if (!BACKWARD_SEXP())
- //
- // We are at the top of the buffer, return the start
- // position and bug out.
- //
- done = 2;
- else if (LOOKING_AT("do[^A-Za-z0-9_$]"))
- //
- // We found an open do
- //
- done = 1;
- else {
- //
- // Otherwise, if we skipped a semicolon, we lose.
- // (Exception: we can skip one semicolon before getting
- // to the last token of the statement, unless that token
- // is a close brace).
- //
- save_excursion = point;
- if (FORWARD_SEXP()) {
- if (!first && character(point - 1) == '}')
- done = 2;
- if (!done && character(point) == ';' &&
- character(point - 1) == '}')
- done = 2;
- if (!done && character(point) == ';') {
- if (first) {
- if (character(point - 1) == ')' && BACKWARD_SEXP()) {
- if (BACKWARD_SEXP() &&
- LOOKING_AT("while") &&
- c_backward_to_start_of_do(limit))
- continue;
- }
- }
- if (!first)
- done = 2;
- first = 0;
- }
- point = save_excursion;
- }
- }
- //
- // If we go too far back in the buffer, we lose.
- //
- if (point < limit && !done)
- done = 2;
- }
- if (done != 1)
- point = startpos;
- return (done == 1);
- }
-
-
- ////////////////////////////////////////////////////////////////////////////
- //
- // c_backward_to_start_of_if will move to the start of the last
- // "unbalanced" `if'.
- //
-
- c_backward_to_start_of_if(limit)
- int limit;
- {
- int if_level = 1;
-
- if (limit < 0)
- limit = 0;
- while (point > 0 && if_level > 0) {
- if (!BACKWARD_SEXP()) {
- say("Syntax error");
- break;
- }
- if (LOOKING_AT("else[^A-Za-z0-9$_]"))
- if_level++;
- else if (LOOKING_AT("if[^A-Za-z0-9$_]"))
- if_level--;
- else if (point < limit) {
- if_level = 0;
- point = limit;
- }
- }
- }
-
-
- ////////////////////////////////////////////////////////////////////////////
- //
- // c_backward_to_noncomment will move in the reverse direction over
- // all whitespace until it runs into some *real* code.
- //
-
- c_backward_to_noncomment(lim)
- int lim;
- {
- int opoint = 0;
- int stop = 0;
-
- if (lim < 0)
- lim = 0;
- while (!stop) {
- //
- // move over the trailing whitespace
- //
- SKIP_CHARS_BACKWARD(" \t\n\f");
- opoint = point;
- //
- // move through standard comments
- //
- if (point >= 2 + lim) {
- point -= 2;
- if (LOOKING_AT("%*/")) {
- if (!SEARCH_BACKWARD("/*"))
- point = lim > 0 ? lim : 0;
- continue;
- }
- point += 2;
- }
- if (point <= lim)
- stop = 1;
- else {
- //
- // Check for C++ style comment
- //
- to_begin_line();
- SKIP_CHARS_FORWARD(" \t");
- if ((character(point) != '#') && !LOOKING_AT("//")) {
- PSTATE ps;
- int from = point;
- int to;
-
- to_end_line();
- to = point;
- point = from;
- parse_partial_cexp(from, to, &ps);
- stop = 1;
- if (ps.incomment) {
- point = ps.beginning_of_comment;
- SKIP_CHARS_BACKWARD(" \t");
- continue;
- }
- }
- point = opoint;
- if (!stop) {
- to_begin_line();
- }
- }
- }
- }
-
-
- ////////////////////////////////////////////////////////////////////////////
- //
- // calculate_c_indent_in_comment will return the appropriate indent
- // for this comment line. This is special cased for block comments.
- //
-
- calculate_c_indent_in_comment()
- {
- int indent = 0;
-
- save_var point;
-
- to_begin_line();
- if (point > 0) {
- SKIP_CHARS_BACKWARD(" \t\n");
- to_begin_line();
- SKIP_CHARS_FORWARD(" \t");
- indent = current_column();
- if (LOOKING_AT("/%*"))
- indent++;
- }
- return (indent);
- }
-
-
- ////////////////////////////////////////////////////////////////////////////
- //
- // calculate_c_indent will return the appropriate indentation for the
- // current line as C code. In usual case an integer is returned: the
- // column to indent to. Returns -1 if line starts inside a string,
- // -2 if in a comment.
- //
-
- calculate_c_indent(state)
- PSTATE *state;
- {
- int parse_start = 0;
- int indent_point;
- int ret = 0;
- int containing_cexp = -1;
- PSTATE ps;
-
- save_var point;
-
- //
- // init
- //
- if (!state) {
- state = &ps;
- state->beginning_of_defun = -1;
- }
- state->containing_cexp = containing_cexp;
- to_begin_line();
- //
- // Check for previous line comment
- //
- if (c_line_comment_align) {
- int save_excursion_1 = point;
- if (nl_reverse()) {
- to_begin_line();
- if (LOOKING_AT("[ \t]*//")) {
- to_indentation();
- return current_column();
- }
- }
- point = save_excursion_1;
- }
- if (point > 0) {
- //
- // parse to the beginning of this function
- //
- indent_point = point;
- if (state->beginning_of_defun == -1) {
- BEGINNING_OF_DEFUN();
- state->beginning_of_defun = point;
- }
- //
- // determine the current level and save the point of the
- // current enclosing C expression
- //
- point = state->beginning_of_defun;
- ret = 0;
- parse_start = point;
- parse_partial_cexp(point, indent_point, state);
- containing_cexp = state->containing_cexp;
- if (state->instring || state->incomment) {
- //
- // return -1 or -2 if this line is part of a standard C style
- // comment block.
- //
- ret = state->quoted;
- } else if (containing_cexp < 0) {
- //
- // Line is at top level. May be data or function
- // definition, or may be function argument declaration.
- // Indent like the previous top level line unless that
- // ends in a closeparen without semicolon, in which case
- // this line is the first argument decl.
- //
- point = indent_point;
- SKIP_CHARS_FORWARD(" \t");
- if (character(point) == '{')
- ret = 0; /* Unless it starts a function body */
- else if (c_argdecl_indent > 0) {
- //
- // Check for non-ansi argument declarations
- //
- c_backward_to_noncomment(parse_start > 0 ? parse_start : 0);
- //
- // look at previous line that's at column 0 to
- // determine whether we are in top-level decls or
- // function's arg decls.
- //
- re_search(-1, "^[^ \f\t\n#]");
- if (LOOKING_AT("[A-Za-z0-9$_]+[^\"\n=]*%(")) {
- point = matchend - 1;
- FORWARD_SEXP();
- SKIP_CHARS_FORWARD(" \t\f");
- if (point < indent_point
- && !index(",;", character(point)))
- ret = c_argdecl_indent;
- }
- }
- } else if (character(containing_cexp) != '{') {
- //
- // line is expression, not statement: indent to just
- // after the surrounding open.
- //
- point = containing_cexp + 1;
- ret = current_column();
- } else {
- //
- // Statement level. Is it a continuation or a new
- // statement Find previous non-comment character.
- //
- point = indent_point;
- c_backward_to_noncomment(containing_cexp);
- //
- // Back up over previous parameter continuations since
- // they don't affect the continuation indent.
- //
- while (character(point-1) == ',' && (point > containing_cexp)) {
- BACKWARD_SEXP();
- to_begin_line();
- c_backward_to_noncomment(containing_cexp);
- }
- //
- // Back up over label lines since they don't affect
- // whether our line is a continuation. Do not back up
- // over case lines or C++ class keywords.
- //
- {
- int stop = 0;
- int save_excursion_1 = point;
-
- while (character(point-1) == ':' &&
- !stop && (point > containing_cexp)) {
- to_indentation();
- if (LOOKING_AT("case"))
- stop = 1;
- else {
- c_backward_to_noncomment(containing_cexp);
- save_excursion_1 = point;
- }
- }
- point = save_excursion_1;
- }
- //
- // Check for a preprocessor statement or its continuation
- // lines. Move back to end of previous non-preprocessor
- // line.
- //
- {
- int found = point;
- int stop = 0;
- int save_excursion_1;
-
- while (!stop) {
- save_excursion_1 = point;
- if (point > 0)
- to_end_line();
- if ((character(point - 1)) != '\\') {
- point = save_excursion_1;
- //
- // This line is not preceded by a backslash.
- // So either it starts a preprocessor command
- // or any following continuation lines should
- // not be skipped.
- //
- to_indentation();
- if (character(point) == '#') {
- to_end_line();
- found = point;
- } else
- stop = 1;
- } else
- nl_reverse();
- }
- point = found;
- //
- // Now we get the answer.
- //
- save_excursion_1 = point;
- point = indent_point;
- SKIP_CHARS_FORWARD(" \t");
- //
- // Don't treat a line with a close-brace as a
- // continuation. It is probably the end of an enum,
- // struct, union or other type declaration.
- //
- if ((character(point)) != '}') {
- point = save_excursion_1;
- if ((point > 0) && !index(",;{}", character(point - 1))) {
- //
- // This line is continuation of preceding
- // line's statement indent
- // c_continued_statement_offset more than the
- // previous line of the statement.
- //
- c_backward_to_exp_start(containing_cexp);
- ret = current_column();
- point = indent_point;
- SKIP_CHARS_FORWARD(" \t");
- if (character(point) != '{')
- ret += c_continued_statement_offset;
- } else
- goto new_statement;
- } else
- new_statement:
- {
- //
- // This line starts a new statement. Position
- // following last unclosed open.
- //
- point = containing_cexp;
- //
- // Is line first statement after an open-brace or
- // after a case. If no, find that first statement
- // and indent like it.
- //
- point++;
- {
- int colon_line_end = 0;
-
- while (SKIP_CHARS_FORWARD(" \t\n"),
- LOOKING_AT("#|/%*|case[ \t\n'(].*:|[a-zA-Z0-9_$]*:")) {
- //
- // Skip over comments and labels
- // following openbrace.
- //
- if (character(point) == '#')
- nl_forward();
- else if (character(point) == '/') {
- point += 2;
- SEARCH_FORWARD("*/");
- } else {
- //
- // case, C++ class keyword or label:
- //
- save_excursion_1 = point;
- to_end_line();
- colon_line_end = point;
- point = save_excursion_1;
- SEARCH_FORWARD(":");
- }
- }
- //
- // The first following code counts if it is
- // before the line we want to indent.
- //
- if (point < indent_point
- && colon_line_end > 0
- && colon_line_end < indent_point) {
- if (colon_line_end > point)
- ret = current_indentation() - c_label_offset;
- else if (colon_line_end > 0
- && colon_line_end > containing_cexp) {
- save_excursion_1 = point;
- point = colon_line_end;
- to_indentation();
- //
- // chack for switch syntax element
- //
- if (LOOKING_AT("case[ \t'(]|default:")) {
- ret = current_column();
- point = indent_point;
- SKIP_CHARS_FORWARD(" \t");
- if (!LOOKING_AT("[{}]"))
- ret += c_indent_level;
- else
- ret -= (c_case_offset +
- c_brace_offset);
- point = save_excursion_1;
- } else {
- point = save_excursion_1;
- ret = current_column();
- }
- } else
- goto no_previous;
- } else
- no_previous:
- {
- //
- // If no previous statement, indent it
- // relative to line brace is on (or the
- // last case statement). For open brace
- // in column zero, don't let statement
- // start there too. If c_indent_level is
- // zero, use c_brace_offset +
- // c_continued_statement_offset instead.
- // For open-braces not the first thing in
- // a line, add in
- // c_brace_imaginary_offset.
- //
- point = containing_cexp;
- if (BOLP() && !c_indent_level)
- ret = c_brace_offset +
- c_continued_statement_offset;
- else if (BOLP())
- ret = c_indent_level;
- else
- ret = c_indent_level - c_brace_offset;
- //
- // Move back over whitespace before the
- // openbrace. If openbrace is not first
- // nonwhite thing on the line, add the
- // c_brace_imaginary_offset.
- //
- SKIP_CHARS_BACKWARD(" \t");
- if (!BOLP())
- ret += c_brace_imaginary_offset;
- //
- // If the openbrace is preceded by a
- // parenthesized exp, move to the
- // beginning of that; possibly a
- // different line.
- //
- if (character(point - 1) == ')')
- BACKWARD_SEXP();
- //
- // Get initial indentation of current line.
- //
- ret += current_indentation();
- }
- }
- }
- }
- }
- }
- return (ret);
- }
-
-
- ////////////////////////////////////////////////////////////////////////////
- //
- // Epsilon Commands
- //
- //
-
- keytable c_tab; /* key table for c mode */
-
- char c_mode_name[] = "NewC";
-
-
- ////////////////////////////////////////////////////////////////////////////
- //
- // c_mode sets up the mode and correctly sets comment hooks
- //
-
- command
- c_mode()
- {
- mode_keys = c_tab; /* use these keys */
- c_tab[')'] = c_tab[']'] = Matchdelim ? (short) show_matching_delimiter : 0;
- indenter = c_indenter;
- auto_indent = 1;
- major_mode = c_mode_name;
- strcpy(comment_start, "(/</|*>)[ \t]*");
- strcpy(comment_pattern, "//.*$|/<*>(.|<newline>)*<*>/<FirstEnd>");
- if (new_c_comments) {
- strcpy(comment_begin, "// ");
- strcpy(comment_end, "");
- } else {
- strcpy(comment_begin, "/* ");
- strcpy(comment_end, " */");
- }
- try_calling("c-mode-hook");
- make_mode();
- }
-
-
- ////////////////////////////////////////////////////////////////////////////
- //
- // do_c_indent is a standard tab indent. Repeated tabs will add tabs
- // into the current location rather than repeating c_indent on every tab.
- //
-
- do_c_indent()
- on c_tab[CTRL('I')]
- {
- int save_excursion = point;
-
- if (!c_tab_always_indent) {
- if (prev_cmd == C_INDENT) /* repeated, make bigger */
- to_column(current_column() + tab_size -
- current_column() % tab_size);
- else {
- /* If not in indentation, do a tab */
- SKIP_CHARS_BACKWARD(" \t");
- if (!BOLP()) {
- point = save_excursion;
- to_column(current_column() + tab_size -
- current_column() % tab_size);
- return;
- }
- point = save_excursion;
- c_indenter();
- }
- } else
- c_indenter();
- this_cmd = C_INDENT;
- }
-
-
- ////////////////////////////////////////////////////////////////////////////
- //
- // do_c_newline is the enter hook. This routine will reindent if the
- // current line is one of several syntax block begins.
- //
-
- do_c_newline()
- on c_tab[CTRL('M')], c_tab[CTRL('J')]
- {
- spot save_excursion = alloc_spot();
-
- if (auto_indent) {
- to_indentation();
- if (LOOKING_AT("#|((case|else)[ \t]*)|[A-Za-z0-9$_]+:|((while|if|switch|for)[ \t]*%(.*%))"))
- c_indenter();
- point = *save_excursion;
- free_spot(save_excursion);
- insert('\n');
- c_indenter();
- } else
- insert('\n');
- }
-
-
- ////////////////////////////////////////////////////////////////////////////
- //
- // c_open and c_close are the open and close block hooks
- //
-
- c_open()
- on c_tab['{']
- {
- normal_character();
- if (auto_indent)
- c_indenter();
- }
-
-
- c_close()
- on c_tab['}']
- {
- normal_character();
- if (auto_indent)
- c_indenter();
- if (Matchdelim)
- find_delimiter();
- }
-
-
- ////////////////////////////////////////////////////////////////////////////
- //
- // forward_cexp and backward_cexp will move to the next (or previous)
- // c_exp in the current function.
- //
-
- command
- forward_cexp()
- on c_tab[ALT(CTRL('F'))]
- {
- int start = point;
- int last = size();
-
- if (!traverse_cexp(iter < 0 ? -1 : 1)) {
- point = start;
- quick_abort();
- }
- while (index(",:;\\", character(point)) && (point < last))
- point++;
- }
-
-
- command
- backward_cexp()
- on c_tab[ALT(CTRL('B'))]
- {
- int start = point;
-
- while ((point > 0) && index(",:;\\ \t\n\f", character(point - 1)))
- point--;
- if (!traverse_cexp(iter < 0 ? 1 : -1)) {
- point = start;
- quick_abort();
- }
- }
-
-
- ////////////////////////////////////////////////////////////////////////////
- //
- // kill_cexp will remove the cexp at the current cursor
- //
-
- command
- kill_cexp()
- on c_tab[ALT(CTRL('K'))]
- {
- int start = point;
- int end;
-
- if (!traverse_cexp(iter < 0 ? -1 : 1)) {
- point = start;
- quick_abort();
- }
- end = point;
- if (end < start) {
- point = end;
- end = start;
- start = point;
- } else
- point = start;
- iter = 0;
- do_save_kill(start, end);
- }
-
-
- ////////////////////////////////////////////////////////////////////////////
- //
- // start_of_defun and end_of_defun will move to the beginning (or
- // end) of the current function.
- //
-
- command
- start_of_defun()
- on c_tab[ALT(CTRL('A'))]
- {
- iter = 0;
- beginning_of_defun();
- window_start = prev_screen_line(3);
- }
-
-
- command
- end_of_defun()
- on c_tab[ALT(CTRL('E'))]
- {
- int orig = point;
-
- iter = 0;
- FORWARD_SEXP();
- point++;
- if (BEGINNING_OF_DEFUN()) {
- while (FORWARD_SEXP() && (character(point - 1) != '}'));
- if (character(point - 1) != '}')
- point = orig;
- else if (character(point) == ';')
- point++;
- }
- }
-
-
- ////////////////////////////////////////////////////////////////////////////
- //
- // up_level will move up one enclosing C block in the current function
- //
-
- command
- up_level()
- on c_tab[ALT(CTRL('U'))]
- {
- PSTATE state;
- int orig = point;
-
- iter = 0;
- BEGINNING_OF_DEFUN();
- while (point <= orig) {
- parse_partial_cexp(point, orig + 1, &state);
- }
- if (state.containing_cexp >= 0)
- point = state.containing_cexp;
- else {
- point = orig;
- error("No containing cexp");
- }
- }
-
-
- ////////////////////////////////////////////////////////////////////////////
- //
- // indent_function will reformat the current function by indenting
- // every line from the beginning of the function.
- //
-
- command
- indent_function()
- on c_tab[ALT(CTRL('Q'))]
- {
- PSTATE ps;
- jmp_buf this_level;
- jmp_buf *old_level = top_level;
- int ret;
- spot stop = 0;
-
- save_spot point;
- save_var case_fold;
-
- iter = 0;
- top_level = &this_level;
- case_fold = 0;
- if (ret = setjmp(top_level)) {
- if (stop)
- free_spot(stop);
- restore_vars();
- top_level = old_level;
- longjmp(top_level, ret);
- }
- beginning_of_defun();
- ps.beginning_of_defun = point;
- end_of_defun();
- stop = alloc_spot();
- point = ps.beginning_of_defun;
- while (point < *stop) {
- c_indenter_1(&ps);
- nl_forward();
- check_abort();
- }
- free_spot(stop);
- top_level = old_level;
- this_cmd = 0;
- }
-
-
- ////////////////////////////////////////////////////////////////////////////
- //
- // mark_function will highlight the current function
- //
-
- command
- mark_function()
- {
- iter = 0;
- beginning_of_defun();
- set_mark();
- end_of_defun();
- exchange_point_and_mark();
- }
-
-
- ////////////////////////////////////////////////////////////////////////////
- //
- // list_functions will show all functions within the current file.
- //
-
- #define FUNCTION_BUFFER "*Functions*"
-
- command
- list_functions()
- on c_tab[ALT('#')]
- {
- char buff[512];
- int brace;
- int thisbuf = bufnum;
- int funcbuf;
- int oldpoint = point;
-
- iter = 0;
- point = 0;
- funcbuf = zap(FUNCTION_BUFFER);
- while (SEARCH_FORWARD("\n{")) {
- brace = point;
- point++;
- beginning_of_defun();
- if (SEARCH_FORWARD("(")) {
- if (point < brace) {
- point--;
- re_search(-1, "[a-zA-Z0-9_]+");
- grab(matchstart, matchend, buff);
- bufnum = funcbuf;
- bprintf("%s\n", buff);
- bufnum = thisbuf;
- }
- }
- point = brace;
- end_of_defun();
- }
- point = oldpoint;
- sort_another(FUNCTION_BUFFER, 0, 0);
- view_buffer(FUNCTION_BUFFER, 1);
- }
-